#include "px_image.h"
#include "pngconf.h"
#include "png.h"
#include "../utility/px_fileutility.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "../utility/px_macros.h"
#include "../utility/px_filebin.h"

typedef struct 
{
	unsigned char* data;
	int size;
	int offset;
}tImageSource;

PXIMAGE* load_BMP(const char* filename)
{
	FILE * fp;
	PXIMAGE* ret;
	int dataOffset;
	//Read the header
	int headerSize ;
	int width;
	int height;
	int bytesPerRow ;
	int size;
	char * pixels =NULL;
	char *  pixels2 =NULL;
    char buffer[2];
	int x,y,c;
	fp =fopen(filename,"rb");
	fread(buffer,1,2,fp);
	assert(buffer[0] == 'B' && buffer[1] == 'M' || !"Not a bitmap file");
	ignore_f(fp,8);
	 dataOffset = read_int_f(fp);
	//Read the header
	headerSize = read_int_f(fp);
	switch(headerSize) {
	case 40:
		//V3
		width = read_int_f(fp);
		height = read_int_f(fp);
		ignore_f(fp,2);
		assert(read_short_f(fp) == 24 || !"Image is not 24 bits per pixel");
		assert(read_short_f(fp)  == 0 || !"Image is compressed");
		break;
	case 12:
		//OS/2 V1
		width  =read_short_f(fp) ;
		height =read_short_f(fp) ;
		ignore_f(fp,2);
		assert(read_short_f(fp)  == 24 || !"Image is not 24 bits per pixel");
		break;
	case 64:
		//OS/2 V2
		assert(!"Can't load OS/2 V2 bitmaps");
		break;
	case 108:
		//Windows V4
		assert(!"Can't load Windows V4 bitmaps");
		break;
	case 124:
		//Windows V5
		assert(!"Can't load Windows V5 bitmaps");
		break;
	default:
		assert(!"Unknown bitmap format");
	}

	//Read the data
	bytesPerRow = ((width * 3 + 3) / 4) * 4 - (width * 3 % 4);
	size = bytesPerRow * height;
    pixels =  (char *)malloc(size);
	fseek(fp,dataOffset,SEEK_SET);
	fread(pixels,1,size,fp);
	//Get the data into the right format
    pixels2 = (char *) malloc (width * height * 3);
	for(y = 0; y < height; y++) {
		for(x = 0; x < width; x++) {
			for(c = 0; c < 3; c++) {
				pixels2[3 * (width * y + x) + c] =
					pixels[bytesPerRow * y + 3 * x + (2 - c)];
			}
		}
	}
    fclose(fp);
	ret = (PXIMAGE *)malloc(sizeof(PXIMAGE));
	ret->filename = (char *)malloc(strlen(filename));
	strcpy(ret->filename ,filename);
	ret->width =width;
	ret->height = height;
	ret->data = pixels2;
	PX_SAFE_FREE(pixels);
	return ret;
}
static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
	tImageSource* isource = (tImageSource*)png_get_io_ptr(png_ptr);

	if((int)(isource->offset + length) <= isource->size)
	{
		memcpy(data, isource->data+isource->offset, length);
		isource->offset += length;
	}
	else
	{
		png_error(png_ptr, "pngReaderCallback failed");
	}
}

PXIMAGE*  _parse_PNG(void * pData, int nDatalen)
{
	png_byte        header[8]   = {0}; 
	png_structp     png_ptr     =   0;
	png_infop       info_ptr    = 0;
	unsigned char * pImateData  = 0;
	PXIMAGE* ret = (PXIMAGE*)malloc(sizeof(PXIMAGE));
	do 
	{
		tImageSource imageSource;
		int         color_type  = 0;
		png_uint_32 nWidth = 0;
		png_uint_32 nHeight = 0;
		int         nBitsPerComponent = 0;
		int bytesPerComponent = 3;
		png_bytep * rowPointers;
		int bytesPerRow ;
		// png header len is 8 bytes
		PX_BREAK_IF(nDatalen < 8);

		// check the data is png or not
		memcpy(header, pData, 8);
		PX_BREAK_IF(png_sig_cmp(header, 0, 8));

		// init png_struct
		png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
		PX_BREAK_IF(! png_ptr);

		// init png_info
		info_ptr = png_create_info_struct(png_ptr);
		PX_BREAK_IF(!info_ptr);

		PX_BREAK_IF(setjmp(png_jmpbuf(png_ptr)));
		// set the read call back function

		
		imageSource.data    = (unsigned char*)pData;
		imageSource.size    = nDatalen;
		imageSource.offset  = 0;
		png_set_read_fn(png_ptr, &imageSource, pngReadCallback);

		// read png
		// PNG_TRANSFORM_EXPAND: perform set_expand()
		// PNG_TRANSFORM_PACKING: expand 1, 2 and 4-bit samples to bytes
		// PNG_TRANSFORM_STRIP_16: strip 16-bit samples to 8 bits
		// PNG_TRANSFORM_GRAY_TO_RGB: expand grayscale samples to RGB (or GA to RGBA)
		png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_PACKING 
			| PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_GRAY_TO_RGB, 0);

	
		png_get_IHDR(png_ptr, info_ptr, &nWidth, &nHeight, &nBitsPerComponent, &color_type, 0, 0, 0);

		// init image info
		//m_bPreMulti = true;
		ret->has_alpha = ( info_ptr->color_type & PNG_COLOR_MASK_ALPHA ) ? PXTURE: PXFALSE;

		//	CCTexture2D::setDefaultAlphaPixelFormat(kCCTexture2DPixelFormat_RGB5A1);

		// allocate memory and read data
		
		if (ret->has_alpha)
		{
			bytesPerComponent = 4;
		}
		pImateData = (unsigned char*)malloc(nHeight * nWidth * bytesPerComponent);
		PX_BREAK_IF(! pImateData); 

		rowPointers = png_get_rows(png_ptr, info_ptr);

		// copy data to image info
		bytesPerRow = nWidth * bytesPerComponent;
		if(ret->has_alpha)
		{
			unsigned int *tmp = (unsigned int *)pImateData;
			int i;
			for(i = 0; i < nHeight; i++)
			{
				int j;
				for(j = 0; j < bytesPerRow; j += 4)
				{
					*tmp++ = CC_RGB_PREMULTIPLY_APLHA( rowPointers[i][j], rowPointers[i][j + 1], 
						rowPointers[i][j + 2], rowPointers[i][j + 3] );
				}
			}
		}
		else
		{
			unsigned int j ;
			for ( j = 0; j < nHeight; ++j)
			{
				memcpy(pImateData + j * bytesPerRow, rowPointers[j], bytesPerRow);
			}
		}

		ret->bits_perComponent = nBitsPerComponent;
		ret->width   = nHeight;
		ret->height  = nWidth;
		ret->data     = pImateData;
		pImateData  = 0;
	} while (0);

	PX_SAFE_FREE(pImateData);

	if (png_ptr)
	{
		png_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : 0, 0);
	}
	return ret;
}


PXIMAGE* load_PNG(const char* filename)
{
	FileBin * fbin =  init_fileBin(filename);
	PXIMAGE* ret = _parse_PNG(fbin->f,fbin->peek_end);
	return ret;
}